package ua.naiksoftware.stomp.dto;

import io.reactivex.annotations.NonNull;
import io.reactivex.annotations.Nullable;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by naik on 05.05.16.
 *
 * @since 2021-04-27
 */
public class StompMessage {
    /**
     * TERMINATE_MESSAGE_SYMBOL
     */
    public static final String TERMINATE_MESSAGE_SYMBOL = "\u0000";
    private static final int NUM_1 = 1;
    private static final int NUM_2 = 2;
    private static final String STRING = "\\n";
    private static final String NULL_STRING = null;
    private static final Pattern PATTERN_HEADER = Pattern.compile("([^:\\s]+)\\s*:\\s*([^:\\s]+)");
    private final String mStompCommand;
    private final List<StompHeader> mStompHeaders;
    private final String mPayload;

    /**
     * StompMessage
     *
     * @param stompCommand
     * @param stompHeaders
     * @param payload
     */
    public StompMessage(String stompCommand, List<StompHeader> stompHeaders, String payload) {
        mStompCommand = stompCommand;
        mStompHeaders = stompHeaders;
        mPayload = payload;
    }

    /**
     * getStompHeaders
     *
     * @return List
     */
    public List<StompHeader> getStompHeaders() {
        return mStompHeaders;
    }

    /**
     * getPayload
     *
     * @return String
     */
    public String getPayload() {
        return mPayload;
    }

    /**
     * getStompCommand
     *
     * @return String
     */
    public String getStompCommand() {
        return mStompCommand;
    }

    /**
     * findHeader
     *
     * @param key key
     * @return String
     */
    @Nullable
    public String findHeader(String key) {
        if (mStompHeaders == null) {
            return NULL_STRING;
        }
        for (StompHeader header : mStompHeaders) {
            if (header.getKey().equals(key)) {
                return header.getValue();
            }
        }
        return NULL_STRING;
    }

    /**
     * compile
     *
     * @return String
     */
    @NonNull
    public String compile() {
        return compile(false);
    }

    /**
     * compile
     *
     * @param isLegacyWhitespace
     * @return String
     */
    @NonNull
    public String compile(boolean isLegacyWhitespace) {
        StringBuilder builder = new StringBuilder();
        builder.append(mStompCommand).append(System.lineSeparator());
        for (StompHeader header : mStompHeaders) {
            builder.append(header.getKey()).append(':').append(header.getValue()).append(System.lineSeparator());
        }
        builder.append(System.lineSeparator());
        if (mPayload != null) {
            builder.append(mPayload);
            if (isLegacyWhitespace) {
                builder.append(System.lineSeparator());
            }
        }
        builder.append(TERMINATE_MESSAGE_SYMBOL);
        return builder.toString();
    }

    /**
     * from
     *
     * @param data
     * @return StompMessage return StompMessage
     */
    public static StompMessage from(@Nullable String data) {
        if (data == null || data.trim().isEmpty()) {
            return new StompMessage(StompCommand.UNKNOWN, null, data);
        }
        Scanner reader = new Scanner(new StringReader(data));
        reader.useDelimiter(STRING);
        String command = reader.next();
        List<StompHeader> headers = new ArrayList<>();

        while (reader.hasNext(PATTERN_HEADER)) {
            Matcher matcher = PATTERN_HEADER.matcher(reader.next());
            matcher.find();
            headers.add(new StompHeader(matcher.group(NUM_1), matcher.group(NUM_2)));
        }
        reader.skip(System.lineSeparator());
        reader.useDelimiter(TERMINATE_MESSAGE_SYMBOL);
        String payload = reader.hasNext() ? reader.next() : null;

        return new StompMessage(command, headers, payload);
    }

    /**
     * toString
     *
     * @return toString
     */
    @Override
    public String toString() {
        return "StompMessage{" + "command='" + mStompCommand + '\'' + ", headers="
                + mStompHeaders + ", payload='" + mPayload + '\'' + '}';
    }
}
